/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "js_method.h"
#include "plugins/ecmascript/runtime/js_tagged_value-inl.h"
#include "libpandafile/method_data_accessor-inl.h"
#include "compiler/compiler_options.h"
#include "runtime/include/profiling_gen.h"

namespace ark::ecmascript {
JSMethod::~JSMethod()
{
    mem::InternalAllocator<>::GetInternalAllocatorFromRuntime()->Free(icMapping_);
}

// It's not allowed '#' token appear in ECMA function(method) name, which discriminates same names in panda methods.
PandaString JSMethod::ParseFunctionName() const
{
    auto *name = GetStringDataAnnotation(Method::AnnotationField::FUNCTION_NAME).data;
    if (name == nullptr) {
        name = GetName().data;
    }
    return utf::Mutf8AsCString(name);
}

void JSMethod::SetCallTypeFromAnnotation()
{
    const panda_file::File *pandaFile = GetPandaFile();
    panda_file::File::EntityId fieldId = GetFileId();
    panda_file::MethodDataAccessor mda(*pandaFile, fieldId);
    mda.EnumerateAnnotations([&](panda_file::File::EntityId annotationId) {
        panda_file::AnnotationDataAccessor ada(*pandaFile, annotationId);
        auto *annotationName = reinterpret_cast<const char *>(pandaFile->GetStringData(ada.GetClassId()).data);
        if (::strcmp("L_ESCallTypeAnnotation;", annotationName) == 0) {
            uint32_t elemCount = ada.GetCount();
            for (uint32_t i = 0; i < elemCount; i++) {
                panda_file::AnnotationDataAccessor::Elem adae = ada.GetElement(i);
                auto *elemName = reinterpret_cast<const char *>(pandaFile->GetStringData(adae.GetNameId()).data);
                if (::strcmp("callType", elemName) == 0) {
                    callType_ = adae.GetScalarValue().GetValue();
                }
            }
        }
    });
}

void JSMethod::InitProfileVector()
{
    const panda_file::File *pandaFile = GetPandaFile();
    panda_file::File::EntityId fieldId = GetFileId();
    panda_file::MethodDataAccessor mda(*pandaFile, fieldId);
    auto profSize = mda.GetProfileSize();
    bool methodTooBig = bytecodeArraySize_ > compiler::g_options.GetCompilerMaxBytecodeSize();
#ifndef NDEBUG
    auto maxInstProfSize = profiling::GetOrderedProfileSizes().back();
    auto maxProfSize = bytecodeArraySize_ * maxInstProfSize;
    LOG_IF(!methodTooBig && !profSize && maxProfSize > panda_file::MethodItem::MAX_PROFILE_SIZE, WARNING, RUNTIME)
        << "Method may be compiled but not profiled because profiling size exceeds limit";
#endif
    if (profSize && !methodTooBig) {
        profileSize_ = profSize.value();
        size_t size = RoundUp(profSize.value(), BITS_PER_INTPTR);
        // NOLINTNEXTLINE(modernize-avoid-c-arrays)
        profilingData_ = MakePandaUnique<uint8_t[]>(size);
        std::memset(profilingData_.get(), 0, size);
    }
}

JSTaggedValue JSMethod::GetLength() const
{
    JSTaggedValue length = JSTaggedValue::Undefined();
    if (GetPandaFile() != nullptr) {
        length = JSTaggedValue(GetNumericalAnnotation(Method::AnnotationField::FUNCTION_LENGTH));
    }
    return length;
}

std::string JSMethod::GetFullName([[maybe_unused]] bool withSignature) const
{
    auto name = ParseFunctionName();
    if (name.empty()) {
        return std::string(Method::GetFullName());
    }
    return std::string(GetClass() != nullptr ? PandaString(GetClass()->GetName()) : "") + "::" + std::string(name);
}
}  // namespace ark::ecmascript
